#include "ZeroSystem.h"
#include "ZeroPopulation.h"
#include "ZeroEnvironment.h"
#include "ZeroThread.h"

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <ctime>

ZeroSystem::ZeroSystem(ZeroPopulation *zpop, ZeroEnvironment *zenv)
{
	mZPop = zpop;
	mZEnv = zenv;
	mZSysThread = NULL;
	mMainMsg = new ZeroMessage();
	mIsRunning = false;
	srand(time(NULL));
}

ZeroSystem::~ZeroSystem()
{
}

void ZeroSystem::start()
{
	if (!mIsRunning) {
		mZSysThread = new ZeroThread(ZeroSystem::Run, this);
		mIsRunning = true;
	}
}

void ZeroSystem::stop()
{
	if (mIsRunning) {
		sendMessage(MSGTYPE_ZSysMain, ZMSG_Stop);
		mZSysThread->join();
		delete mZSysThread;
		mIsRunning = false;
	}
}

bool ZeroSystem::probCrossover()
{
	return (rand() % 100) >= 10;
}

bool ZeroSystem::probCopyMutation()
{
	return (rand() % 100) >= 60;
}

bool ZeroSystem::probIsometric()
{
	return (rand() % 100) >= 90;
}

ZeroPopulation* ZeroSystem::getZPop()
{
	return mZPop;
}

ZeroEnvironment* ZeroSystem::getZEnv()
{
	return mZEnv;
}

ZeroSystem::ZSysMsg_e ZeroSystem::getMessage(ZMsgType_e msgType)
{
	ZSysMsg_e msg;
	switch (msgType) {
	case MSGTYPE_ZSysMain:
		msg = (ZSysMsg_e)mMainMsg->getMessage();
		break;
	}
	return msg;
}

void ZeroSystem::sendMessage(ZMsgType_e msgType, ZSysMsg_e msg)
{
	switch (msgType) {
	case MSGTYPE_ZSysMain:
		mMainMsg->postMessage(msg);
		break;
	}
}

void* ZeroSystem::Run(void *data)
{
	ZeroSystem *me = (ZeroSystem*)data;
	ZeroPopulation *zpop = me->getZPop();
	ZeroEnvironment *zenv = me->getZEnv();
	int popSize = zpop->getSize();
	int newPopCount, targetNewPopCount = 10;
	for (int generation = 0;;generation++) {
		ZSysMsg_e msg;
		while ((msg = me->getMessage(MSGTYPE_ZSysMain)) != ZMSG_None) {
			switch (msg) {
			case ZMSG_NewEnv:
				break;
			case ZMSG_Stop:
				std::cout << "now stopping..." << std::endl;
				break;
			}
			if (msg == ZMSG_Stop)
				break;
		}
		if (msg == ZMSG_Stop)
			break;
		// Produce new offsprings by crossover and mutation
		newPopCount = 0;
		while (newPopCount < targetNewPopCount) {
			ZeroEntity *a, *b, *c;
			if (zpop->getSize() > 1 && me->probCrossover()) {
				a = zpop->prioritySelect(3);
				b = zpop->prioritySelect(2);
				for (int i = 0;i < 15;i++) {
					c = a->mate(b);
					if (zenv->isValid(c)) {
						std::cout << "new crossover: (" << c->getGene(0)->getGeneFSize() << ", " << c->getGene(0)->getGeneMSize() << ")" << std::endl;
						zenv->evaluate(c);
						zpop->storeEntity(c);
						newPopCount++;
					} else {
						delete c;
					}
				}
			}
			if (zpop->getSize() > 0 && me->probCopyMutation()) {
				a = zpop->prioritySelect(3);
				for (int i = 0;i < 5;i++) {
					c = a->copy();
					if (zenv->isValid(c)) {
						std::cout << "new copy: (" << c->getGene(0)->getGeneFSize() << ", " << c->getGene(0)->getGeneMSize() << ")" << std::endl;
						zenv->evaluate(c);
						zpop->storeEntity(c);
						newPopCount++;
					} else {
						delete c;
					}
				}
			}
			if (me->probIsometric()) {
				for (int i = 0;i < 2;i++) {
					c = ZeroEntity::RandomEntity(1);
					if (zenv->isValid(c)) {
						std::cout << "new random: (" << c->getGene(0)->getGeneFSize() << ", " << c->getGene(0)->getGeneMSize() << ")" << std::endl;
						zenv->evaluate(c);
						zpop->storeEntity(c);
						newPopCount++;
					} else {
						delete c;
					}
				}
			}

		}
		// merge together...
		zpop->merge();
		// selection...
		zenv->selection(zpop);
		usleep(50000);
	};
	return (void*)0;
}

void ZeroSystem::cleanup()
{
}

void ZeroSystem::dumpStatus()
{
	mZPop->dumpStatus();
}
